
package com.stary.pay.unionpay.api.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.stary.pay.unionpay.api.UnionpayConstants;
import com.stary.pay.wxpay.api.util.StringUtils;

/**
 * <p>unionpay工具类</p>
 * @author stary {@link stary1993@qq.com}
 * @since 2019-6-20
 */
public class UnionpayUtils {

	public static Logger getLogger() {
		return LoggerFactory.getLogger("« unionpay api »");
	}
	/**
	 * 文件拷贝方法
	 * @param srcFile 源文件
	 * @param destFile 目标文件
	 * @return
	 * @throws IOException
	 */
	public static boolean copyFile(String srcFile, String destFile) {
		boolean flag = false;
		FileInputStream fin = null;
		FileOutputStream fout = null;
		FileChannel fcin = null;
		FileChannel fcout = null;
		try {
			// 获取源文件和目标文件的输入输出流
			fin = new FileInputStream(srcFile);
			fout = new FileOutputStream(destFile);
			// 获取输入输出通道
			fcin = fin.getChannel();
			fcout = fout.getChannel();
			// 创建缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			while (true) {
				// clear方法重设缓冲区，使它可以接受读入的数据
				buffer.clear();
				// 从输入通道中将数据读到缓冲区
				int r = fcin.read(buffer);
				// read方法返回读取的字节数，可能为零，如果该通道已到达流的末尾，则返回-1
				if (r == -1) {
					flag = true;
					break;
				}
				// flip方法让缓冲区可以将新读入的数据写入另一个通道
				buffer.flip();
				// 从输出通道中将数据写入缓冲区
				fcout.write(buffer);
			}
			fout.flush();
		} catch (IOException ie) {
			getLogger().error("copy file fail", ie);
		} finally {
			try {
				if (null != fin) {
					fin.close();
				}
				if (null != fout) {
					fout.close();
				}
				if (null != fcin) {
					fcin.close();
				}
				if (null != fcout) {
					fcout.close();
				}
			} catch (IOException ie) {
				getLogger().error("releases any system resources fail", ie);
			}
		}
		return flag;
	}

	/**
	 * 写文件方法
	 * @param filePath 文件路径
	 * @param fileContent 文件内容
	 * @param encoding 编码
	 * @return
	 */
	public static boolean writeFile(String filePath, String fileContent, String encoding) {
		FileOutputStream fout = null;
		FileChannel fcout = null;
		File file = new File(filePath);
		if (file.exists()) {
			file.delete();
		}		
		try {
			fout = new FileOutputStream(filePath);
			// 获取输出通道
			fcout = fout.getChannel();
			// 创建缓冲区
			// ByteBuffer buffer = ByteBuffer.allocate(1024);
			ByteBuffer buffer = ByteBuffer.wrap(fileContent.getBytes(encoding));
			fcout.write(buffer);
			fout.flush();
		} catch (FileNotFoundException e) {
			getLogger().error("write file fail", e);
			return false;
		} catch (IOException ie) {
			getLogger().error("write file fail", ie);
			return false;
		} finally {
			try {
				if (null != fout) {
					fout.close();
				}
				if (null != fcout) {
					fcout.close();
				}
			} catch (IOException ie) {
				getLogger().error("releases any system resources fail", ie);
				return false;
			}
		}
		return true;
	}

	/**
	 * 将传入的文件名(xxx)改名 <br>
	 * 结果为：xxx_backup.cer
	 * @param fileName
	 * @return
	 */
	public static String genBackupName(String fileName) {
		if (StringUtils.isEmpty(fileName)) {
			return "";
		}
		int i = fileName.lastIndexOf(UnionpayConstants.POINT);
		String leftFileName = fileName.substring(0, i);
		String rightFileName = fileName.substring(i + 1);
		String newFileName = leftFileName + "_backup" + UnionpayConstants.POINT + rightFileName;
		return newFileName;
	}

	public static byte[] readFileByNIO(String filePath) {
		FileInputStream in = null;
		FileChannel fc = null;
		ByteBuffer bf = null;
		try {
			in = new FileInputStream(filePath);
			fc = in.getChannel();
			bf = ByteBuffer.allocate((int) fc.size());
			fc.read(bf);
			return bf.array();
		} catch (Exception e) {
			getLogger().error(e.getMessage());
			return null;
		} finally {
			try {
				if (null != fc) {
					fc.close();
				}
				if (null != in) {
					in.close();
				}
			} catch (Exception e) {
				getLogger().error(e.getMessage());
				return null;
			}
		}
	}

	/**
	 * 过滤请求报文中的空字符串或者空字符串
	 * @param contentData
	 * @return
	 */
	public static UnionpayHashMap filterBlank(UnionpayHashMap contentData) {
		getLogger().debug("print request message domain:");
		UnionpayHashMap submitFromData = new UnionpayHashMap();
		Set<String> keyset = contentData.keySet();		
		for (String key : keyset) {
			String value = contentData.get(key);
			if (!StringUtils.isEmpty(value)) {
				// 对value值进行去除前后空处理
				submitFromData.put(key, value.trim());
				getLogger().debug(key + "-->" + String.valueOf(value));
			}
		}
		return submitFromData;
	}

	/**
	 * 功能：将结果文件内容 转换成明文字符串：解base64,解压缩<br/>
	 * 适用到的交易：批量交易状态查询
	 * @param fileContent 批量交易状态查询返回的文件内容
	 * @return 内容明文<br>
	 */
	public static String getFileContent(String fileContent,String encoding) {
		String fc = "";
		try {
			fc = new String(inflater(UnionpayEncrypt.base64Decode(fileContent.getBytes())), encoding);
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (IOException ie) {
			getLogger().error(ie.getMessage(), ie);
		}
		return fc;
	}
	/**
	 * 解压缩.
	 * @param inputByte byte[]数组类型的数据
	 * @return 解压缩后的数据
	 * @throws IOException
	 */
	private static byte[] inflater(final byte[] inputByte) throws IOException {
		int compressedDataLength = 0;
		Inflater compresser = new Inflater(false);
		compresser.setInput(inputByte, 0, inputByte.length);
		ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
		byte[] result = new byte[1024];
		try {
			while (!compresser.finished()) {
				compressedDataLength = compresser.inflate(result);
				if (compressedDataLength == 0) {
					break;
				}
				o.write(result, 0, compressedDataLength);
			}
		} catch (Exception e) {
			getLogger().error("data format error!\n" , e);
		} finally {
			o.close();
		}
		compresser.end();
		return o.toByteArray();
	}

	/**
	 * 压缩.
	 * @param inputByte 需要解压缩的byte[]数组
	 * @return 压缩后的数据
	 * @throws IOException
	 */
	public static byte[] deflater(final byte[] inputByte) throws IOException {
		int compressedDataLength = 0;
		Deflater compresser = new Deflater();
		compresser.setInput(inputByte);
		compresser.finish();
		ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
		byte[] result = new byte[1024];
		try {
			while (!compresser.finished()) {
				compressedDataLength = compresser.deflate(result);
				o.write(result, 0, compressedDataLength);
			}
		} finally {
			o.close();
		}
		compresser.end();
		return o.toByteArray();
	}
	/**
	 * 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>
	 * 更新成功则返回1，无更新返回0，失败异常返回-1<br>
	 * @param resData 返回报文
	 * @param encryptCertFile 本地证书路径
	 * @param encoding
	 * @return
	 */
	public static int updateEncryptCert(UnionpayHashMap resData, String encryptCertFile, String encoding) {
		return UnionpaySignature.getEncryptCert(resData, encryptCertFile, encoding);
	}

	/**
	 * 获取Luhn
	 * @param number
	 * @return
	 */
	public static int genLuhn(String number) {
		return UnionpayEncrypt.genLuhn(number);
	}

	/**
	 * 
	 * 有卡交易信息域(cardTransData)构造<br/>
	 * 所有子域需用“{}”包含，子域间以“&”符号链接。格式如下：{子域名1=值&子域名2=值&子域名3=值}
	 * @param cardTransDataMap cardTransData的数据
	 * @param requestData 必须包含merId、orderId、txnTime、txnAmt，磁道加密时需要使用
	 * @param encoding 编码
	 * @return
	 */
	public static String getCardTransData(UnionpayHashMap cardTransDataMap, 
			UnionpayHashMap requestData, String encryptCertPath, String encoding) {
		StringBuffer cardTransDataBuffer = new StringBuffer();
		if (cardTransDataMap.containsKey("track2Data")) {
			StringBuffer track2Buffer = new StringBuffer();
			track2Buffer.append(requestData.get("merId"))
			.append(UnionpayConstants.COLON).append(requestData.get("orderId"))
			.append(UnionpayConstants.COLON).append(requestData.get("txnTime"))
			.append(UnionpayConstants.COLON).append(requestData.get("txnAmt") == null ? 0 : requestData.get("txnAmt"))
			.append(UnionpayConstants.COLON).append(cardTransDataMap.get("track2Data"));
			cardTransDataMap.put("track2Data", encryptData(track2Buffer.toString(), encryptCertPath, encoding));
		}
		if (cardTransDataMap.containsKey("track3Data")) {
			StringBuffer track3Buffer = new StringBuffer();
			track3Buffer.append(requestData.get("merId"))
			.append(UnionpayConstants.COLON).append(requestData.get("orderId"))
			.append(UnionpayConstants.COLON).append(requestData.get("txnTime"))
			.append(UnionpayConstants.COLON).append(requestData.get("txnAmt") == null ? 0 : requestData.get("txnAmt"))
			.append(UnionpayConstants.COLON).append(cardTransDataMap.get("track3Data"));
			cardTransDataMap.put("track3Data", encryptData(track3Buffer.toString(), encryptCertPath,	encoding));
		}
		return cardTransDataBuffer.append(UnionpayConstants.LEFT_BRACE)
				.append(coverMapToString(cardTransDataMap))
				.append(UnionpayConstants.RIGHT_BRACE).toString();
	}


	/**
	 * 获取敏感信息加密证书的物理序列号<br>
	 * @return
	 */
	public static String getEncryptCertId(String encryptCertPath) {
		return UnionpayCertHolder.getEncryptCertId(encryptCertPath);
	}

	/**
	 * 对字符串做base64<br>
	 * @param rawStr<br>
	 * @param encoding<br>
	 * @return<br>
	 * @throws IOException
	 */
	public static String base64Encode(String rawStr,String encoding) throws IOException {
		byte [] rawByte = rawStr.getBytes(encoding);
		return new String(UnionpayEncrypt.base64Encode(rawByte), encoding);
	}

	/**
	 * 对base64的字符串解base64<br>
	 * @param base64Str<br>
	 * @param encoding<br>
	 * @return<br>
	 * @throws IOException
	 */
	public static String base64Decode(String base64Str,String encoding) throws IOException {
		byte [] rawByte = base64Str.getBytes(encoding);
		return new String(UnionpayEncrypt.base64Decode(rawByte), encoding);	
	}


	/**
	 * 功能：持卡人信息域customerInfo构造，勾选对敏感信息加密权限 适用新加密规范，对pin和phoneNo，cvn2，expired加密 <br>
	 * 适用到的交易： <br>
	 * @param customerInfoMap 信息域请求参数 key送域名value送值,必送 <br>
	 *        例如：customerInfoMap.put("certifTp", "01");					//证件类型 <br>
				  customerInfoMap.put("certifId", "341126197709218366");	//证件号码 <br>
				  customerInfoMap.put("customerNm", "互联网");				//姓名 <br>
				  customerInfoMap.put("smsCode", "123456");					//短信验证码 <br>
				  customerInfoMap.put("pin", "111111");						//密码（加密） <br>
				  customerInfoMap.put("phoneNo", "13552535506");			//手机号（加密） <br>
				  customerInfoMap.put("cvn2", "123");           			//卡背面的cvn2三位数字（加密） <br>
				  customerInfoMap.put("expired", "1711");  				    //有效期 年在前月在后（加密） <br>
	 * @param accNo  customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码PIN，此字段可以不送<br>
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return base64后的持卡人信息域字段 <br>
	 */
	public static String getCustomerInfoWithEncrypt(UnionpayHashMap customerInfoMap, String accNo, String encryptCertPath, String encoding) {
		if (customerInfoMap.isEmpty()) {
			return "{}";
		}
		StringBuffer sf = new StringBuffer(UnionpayConstants.LEFT_BRACE);
		//敏感信息加密域
		StringBuffer encryptedInfoSb = new StringBuffer("");
		for (Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();) {
			String key = it.next();
			String value = customerInfoMap.get(key);
			if (key.equals("phoneNo") || key.equals("cvn2") || key.equals("expired")) {
				encryptedInfoSb.append(key).append(UnionpayConstants.EQUAL).append(value).append(UnionpayConstants.AMPERSAND);
			} else {
				if (key.equals("pin")) {
					if (!StringUtils.isEmpty(accNo)) {
						getLogger().error("send the password(PIN),accNo is must not null.");
						throw new RuntimeException("accNo is must not null.");
					} else {
						value = encryptPin(accNo, value, encryptCertPath, encoding);
					}
				}
				sf.append(key).append(UnionpayConstants.EQUAL).append(value).append(UnionpayConstants.AMPERSAND);
			}
		}
		if (!StringUtils.isEmpty(encryptedInfoSb.toString())) {
			encryptedInfoSb.setLength(encryptedInfoSb.length() - 1);//去掉最后一个&符号
			getLogger().debug("customerInfo encryptedInfo:" + encryptedInfoSb.toString());
			sf.append("encryptedInfo").append(UnionpayConstants.EQUAL).append(encryptData(encryptedInfoSb.toString(), encryptCertPath, encoding));
		} else {
			sf.setLength(sf.length() - 1);
		}
		String customerInfo = sf.append(UnionpayConstants.RIGHT_BRACE).toString();
		getLogger().debug("customerInfo:" + customerInfo);
		try {
			return new String(UnionpayEncrypt.base64Encode(sf.toString().getBytes(encoding)), encoding);
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (IOException ie) {
			getLogger().error(ie.getMessage(), ie);
		}
		return customerInfo;
	}

	/**
	 * 解析返回报文（后台通知）中的customerInfo域：<br>
	 * 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
	 * @param customerInfo<br>
	 * @param encoding<br>
	 * @return
	 */
	public static UnionpayHashMap parseCustomerInfo(String customerInfo, String signCertPwd, String encoding) {
		UnionpayHashMap customerInfoMap = null;
		try {
			byte[] b = UnionpayEncrypt.base64Decode(customerInfo.getBytes(encoding));
			String customerInfoNoBase64 = new String(b,encoding);
			getLogger().debug("base64 decode result:" + customerInfoNoBase64);
			//去掉前后的{}
			customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
			customerInfoMap = parseQString(customerInfoNoBase64);
			if (customerInfoMap.containsKey("encryptedInfo")) {
				String encInfoStr = customerInfoMap.get("encryptedInfo");
				customerInfoMap.remove("encryptedInfo");
				String encryptedInfoStr = decryptData(encInfoStr, signCertPwd, encoding);
				UnionpayHashMap encryptedInfoMap = parseQString(encryptedInfoStr);
				customerInfoMap.putAll(encryptedInfoMap);
			}
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (IOException ie) {
			getLogger().error(ie.getMessage(), ie);
		}
		return customerInfoMap;
	}

	/**
	 * 解析返回报文（后台通知）中的customerInfo域：<br>
	 * 解base64，如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
	 * @param customerInfo<br>
	 * @param encoding<br>
	 * @return
	 */
	public static UnionpayHashMap parseCustomerInfo(String customerInfo, String certPath, 
			String certPwd, String encoding) {
		UnionpayHashMap customerInfoMap = null;
		try {
			byte[] b = UnionpayEncrypt.base64Decode(customerInfo.getBytes(encoding));
			String customerInfoNoBase64 = new String(b,encoding);
			getLogger().debug("base64 decode result:" +customerInfoNoBase64);
			//去掉前后的{}
			customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
			customerInfoMap = parseQString(customerInfoNoBase64);
			if(customerInfoMap.containsKey("encryptedInfo")){
				String encInfoStr = customerInfoMap.get("encryptedInfo");
				customerInfoMap.remove("encryptedInfo");
				String encryptedInfoStr = decryptData(encInfoStr, certPath, certPwd, encoding);
				UnionpayHashMap encryptedInfoMap = parseQString(encryptedInfoStr);
				customerInfoMap.putAll(encryptedInfoMap);
			}
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (IOException e) {
			getLogger().error(e.getMessage(), e);
		}
		return customerInfoMap;
	}

	/**
	 * 密码加密并做base64
	 * @param accNo 卡号
	 * @param pwd 密码
	 * @param encoding
	 * @return 加密的内容
	 */
	public static String encryptPin(String accNo, String pin, String encryptCertPath, String encoding) {
		return UnionpayEncrypt.encryptPin(accNo, pin, encoding, 
				UnionpayCertHolder.getEncryptCertPublicKey(encryptCertPath));
	}

	/**
	 * 敏感信息加密并做base64（卡号，手机号，cvn2，有效期）
	 * @param data 送 phoneNo,cvn2,有效期
	 * @param encoding
	 * @return 加密的密文
	 */
	public static String encryptData(String data, String encryptCertPath, String encoding) {
		return UnionpayEncrypt.encryptData(data, encoding, 
				UnionpayCertHolder.getEncryptCertPublicKey(encryptCertPath));
	}

	/**
	 * 敏感信息解密，使用配置文件acp_sdk.properties解密
	 * @param base64EncryptedInfo 加密信息
	 * @param encoding
	 * @return 解密后的明文
	 */
	public static String decryptData(String base64EncryptedInfo, String signCertPwd, String encoding) {
		return UnionpayEncrypt.decryptData(base64EncryptedInfo, encoding, 
				UnionpayCertHolder.getSignCertPrivateKey(signCertPwd));
	}

	/**
	 * 敏感信息解密,通过传入的私钥解密
	 * @param base64EncryptedInfo 加密信息
	 * @param certPath 私钥文件（带全路径）
	 * @param certPwd 私钥密码
	 * @param encoding
	 * @return
	 */
	public static String decryptData(String base64EncryptedInfo, String certPath, 
			String certPwd, String encoding) {
		return UnionpayEncrypt.decryptData(base64EncryptedInfo, encoding, 
				UnionpayCertHolder.getSignCertPrivateKeyByStoreMap(certPath, certPwd));
	}

	/**
	 * 5.0.0加密磁道信息，5.1.0接口请勿使用
	 * @param trackData 待加密磁道数据
	 * @param encoding 编码格式
	 * @return 加密的密文
	 * @deprecated
	 */
	public static String encryptTrack(String trackData,String encryptTrackKeyModulus, String encryptTrackKeyExponent, String encoding) {
		return UnionpayEncrypt.encryptData(trackData, encoding,
				UnionpayCertHolder.getEncryptTrackPublicKey(encryptTrackKeyModulus, encryptTrackKeyExponent));
	}

	/**
	 * 功能：前台交易构造HTTP POST自动提交表单
	 * @param action 表单提交地址
	 * @param hiddens 以MAP形式存储的表单键值
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return 构造好的HTTP POST交易表单
	 */
	public static String createAutoFormHtml(String reqUrl, UnionpayHashMap hiddens,String encoding) {
		StringBuffer sf = new StringBuffer();
		sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset="+encoding+"\"/></head><body>");
		sf.append("<form id = \"pay_form\" action=\"" + reqUrl + "\" method=\"post\">");
		if (null != hiddens && 0 != hiddens.size()) {
			Set<Entry<String, String>> set = hiddens.entrySet();
			Iterator<Entry<String, String>> it = set.iterator();
			while (it.hasNext()) {
				Entry<String, String> ey = it.next();
				String key = ey.getKey();
				String value = ey.getValue();
				sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\"" + key + "\" value=\"" + value + "\"/>");
			}
		}
		sf.append("</form>");
		sf.append("</body>");
		sf.append("<script type=\"text/javascript\">");
		sf.append("document.all.pay_form.submit();");
		sf.append("</script>");
		sf.append("</html>");
		return sf.toString();
	}


	/**
	 * 功能：将批量文件内容使用DEFLATE压缩算法压缩，Base64编码生成字符串并返回<br/>
	 * 适用到的交易：批量代付，批量代收，批量退货
	 * @param filePath 批量文件-全路径文件名
	 * @return
	 */
	public static String enCodeFileContent(String filePath, String encoding) {
		String baseFileContent = "";

		File file = new File(filePath);
		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				getLogger().error(e.getMessage(), e);
			}
		}
		InputStream in = null;
		try {
			in = new FileInputStream(file);
			int fl = in.available();
			if (null != in) {
				byte[] s = new byte[fl];
				in.read(s, 0, fl);
				// 压缩编码.
				baseFileContent = new String(UnionpayEncrypt.base64Encode(UnionpayUtils.deflater(s)), encoding);
			}
		} catch (Exception e) {
			getLogger().error(e.getMessage(), e);
		} finally {
			if (null != in) {
				try {
					in.close();
				} catch (IOException e) {
					getLogger().error(e.getMessage(), e);
				}
			}
		}
		return baseFileContent;
	}

	/**
	 * 功能：解析交易返回的fileContent字符串并落地 （ 解base64，解DEFLATE压缩并落地）<br/>
	 * 适用到的交易：对账文件下载，批量交易状态查询
	 * @param resData 返回报文map
	 * @param fileDirectory 落地的文件目录（绝对路径）
	 * @param encoding 上送请求报文域encoding字段的值
	 */
	public static String deCodeFileContent(UnionpayHashMap resData, String fileDirectory, String encoding) {
		// 解析返回文件
		String filePath = null;
		String fileContent = resData.get(UnionpayConstants.FILECONTENT);
		if (!StringUtils.isEmpty(fileContent)) {
			FileOutputStream out = null;
			try {
				byte[] fileArray = UnionpayUtils.inflater(
						UnionpayEncrypt.base64Decode(fileContent.getBytes(encoding)));
				if (StringUtils.isEmpty(resData.get(UnionpayConstants.FILENAME))) {
					filePath = fileDirectory + File.separator + resData.get(UnionpayConstants.MERID)
							+ UnionpayConstants.UNLINE + resData.get(UnionpayConstants.BATCHNO) + UnionpayConstants.UNLINE
							+ resData.get(UnionpayConstants.TXNTIME) + UnionpayConstants.FILE_SUFFIX_TXT;
				} else {
					filePath = fileDirectory + File.separator + resData.get(UnionpayConstants.FILENAME);
				}
				File file = new File(filePath);
				if (file.exists()) {
					file.delete();
				}
				file.createNewFile();
				out = new FileOutputStream(file);
				out.write(fileArray, 0, fileArray.length);
				out.flush();
			} catch (UnsupportedEncodingException e) {
				getLogger().error(e.getMessage(), e);
			} catch (IOException ie) {
				getLogger().error(ie.getMessage(), ie);
			} finally {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return filePath;
	}

	/**
	 * 功能：持卡人信息域customerInfo构造<br/>
	 * 说明：不勾选对敏感信息加密权限使用旧的构造customerInfo域方式，不对敏感信息进行加密（对 phoneNo，cvn2， expired不加密），但如果送pin的话则加密<br/>
	 * @param customerInfoMap 信息域请求参数 key送域名value送值,必送<br/>
	 *  	 例如：customerInfoMap.put("certifTp", "01");					// 证件类型<br/>
				  customerInfoMap.put("certifId", "341126197709218366");	// 证件号码<br/>
				  customerInfoMap.put("customerNm", "互联网");				// 姓名<br/>
				  customerInfoMap.put("phoneNo", "13552535506");			// 手机号<br/>
				  customerInfoMap.put("smsCode", "123456");					// 短信验证码<br/>
				  customerInfoMap.put("pin", "111111");						// 密码（加密）<br/>
				  customerInfoMap.put("cvn2", "123");           			// 卡背面的cvn2三位数字（不加密）<br/>
				  customerInfoMap.put("expired", "1711");  				    // 有效期 年在前月在后（不加密)
	 * @param accNo  customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码pin，此字段可以不送
	 * @param encoding 上送请求报文域encoding字段的值			  
	 * @return base64后的持卡人信息域字段
	 */
	public static String getCustomerInfo(UnionpayHashMap customerInfoMap, String accNo, String encryptCertPath, String encoding) {
		if (customerInfoMap.isEmpty()) {
			return "{}";
		}
		StringBuffer sf = new StringBuffer(UnionpayConstants.LEFT_BRACE);
		for (Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();) {
			String key = it.next();
			String value = customerInfoMap.get(key);
			if (key.equals("pin")) {
				if (StringUtils.isEmpty(accNo)) {
					getLogger().error("send the password(PIN),accNo is must not null.");
					throw new RuntimeException("accNo is must not null.");
				} else {
					value = encryptPin(accNo, value, encryptCertPath, encoding);
				}
			}
			sf.append(key).append(UnionpayConstants.EQUAL).append(value);
			if (it.hasNext()) {
				sf.append(UnionpayConstants.AMPERSAND);
			}
		}
		String customerInfo = sf.append(UnionpayConstants.RIGHT_BRACE).toString();
		getLogger().debug("customerInfo:" + customerInfo);
		try {
			return new String(UnionpayEncrypt.base64Encode(sf.toString().getBytes(encoding)), encoding);
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (IOException ie) {
			getLogger().error(ie.getMessage(), ie);
		}
		return customerInfo;
	}
	/**
	 * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
	 * 功能：对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
	 * @param reqData 请求报文map<br>
	 * @param encoding 上送请求报文域encoding字段的值<br>
	 * @return　签名后的map对象<br>
	 */
	public static UnionpayHashMap sign(UnionpayHashMap reqData,String secureKey, String signCertPwd, String encoding) {
		reqData = filterBlank(reqData);
		UnionpaySignature.sign(reqData, secureKey, signCertPwd, encoding);
		return reqData;
	}
	/**
	 * 多证书签名(通过传入私钥证书路径和密码签名）<br/>
	 * 功能：如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(singleMode=false)
	 * @param reqData 请求报文map
	 * @param certPath 签名私钥文件（带路径）
	 * @param certPwd 签名私钥密码
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return　签名后的map对象
	 */
	public static UnionpayHashMap signByCertInfo(UnionpayHashMap reqData, 
			String certPath, String certPwd, String encoding) {
		reqData = filterBlank(reqData);
		UnionpaySignature.signByCertInfo(reqData, certPath, certPwd, encoding);
		return reqData;
	}

	/**
	 * 多密钥签名(通过传入密钥签名)<br/>
	 * 功能：如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(singleMode=false)
	 * @param reqData 请求报文map
	 * @param secureKey 签名对称密钥
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return　签名后的map对象
	 */
	public static UnionpayHashMap signBySecureKey(UnionpayHashMap reqData, 
			String secureKey, String encoding) {
		reqData = UnionpayUtils.filterBlank(reqData);
		UnionpaySignature.signBySecureKey(reqData, secureKey, encoding);
		return reqData;
	}

	/**
	 * 验证签名(SHA-1摘要算法)
	 * @param resData 返回报文数据
	 * @param secureKey 秘钥
	 * @param validateCertDir
	 * @param rootCertFile
	 * @param middleCertFile
	 * @param isCheckCNName
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return true 通过 false 未通过
	 */
	public static boolean checkSign(UnionpayHashMap rspData, String secureKey, String validateCertDir, 
			String rootCertFile, String middleCertFile, boolean isCheckCNName, String encoding) {
		return UnionpaySignature.validate(
				rspData, secureKey, validateCertDir, rootCertFile, middleCertFile, isCheckCNName, encoding);
	}

	/**
	 * 多密钥验签(通过传入密钥签名)
	 * @param resData 返回报文数据
	 * @param encoding 上送请求报文域encoding字段的值
	 * @return true 通过 false 未通过
	 */
	public static boolean validateBySecureKey(UnionpayHashMap rspData, String secureKey, String encoding) {
		return UnionpaySignature.validateBySecureKey(rspData, secureKey, encoding);
	}

	/**
	 * 5.1.0开发包已删除此方法，请直接参考5.1.0开发包中的VerifyAppData.java验签。<br/>
	 * 对控件支付成功返回的结果信息中data域进行验签（控件端获取的应答信息）
	 * @param jsonData json格式数据，例如：{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==",  "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}
	 * @return 是否成功
	 * @deprecated
	 */
	public static boolean validateAppResponse(String jsonData, String signMethod, String validateCertDir, String encoding) {
		getLogger().info("control response information check start processing:[" + jsonData + "]");
		if (StringUtils.isEmpty(encoding)) {
			encoding = UnionpayConstants.CHARSET_UTF8;
		}
		Pattern p = Pattern.compile("\\s*\"sign\"\\s*:\\s*\"([^\"]*)\"\\s*");
		Matcher m = p.matcher(jsonData);
		if (!m.find()) {
			return false;
		}
		String sign = m.group(1);

		p = Pattern.compile("\\s*\"data\"\\s*:\\s*\"([^\"]*)\"\\s*");
		m = p.matcher(jsonData);
		if (!m.find()) {
			return false;
		}
		String data = m.group(1);

		p = Pattern.compile("cert_id=(\\d*)");
		m = p.matcher(jsonData);
		if (!m.find()) {
			return false;
		}
		String certId = m.group(1);
		try {
			// 验证签名需要用银联发给商户的公钥证书.
			return UnionpayEncrypt.validateSignBySoft(UnionpayCertHolder.getValidatePublicKey(certId, signMethod, validateCertDir), 
					UnionpayEncrypt.base64Decode(sign.getBytes(encoding)), 
					UnionpayEncrypt.sha1X16(data, encoding));
		} catch (UnsupportedEncodingException e) {
			getLogger().error(e.getMessage(), e);
		} catch (Exception e) {
			getLogger().error(e.getMessage(), e);
		}
		return false;
	}

	/**
	 * 将Map中的数据转换成key1=value1&key2=value2的形式 不包含签名域signature
	 * 
	 * @param data
	 *            待拼接的Map数据
	 * @return 拼接好后的字符串
	 */
	public static String coverMapToString(UnionpayHashMap data) {
		TreeMap<String, String> tree = new TreeMap<String, String>();
		Iterator<Entry<String, String>> it = data.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			if (UnionpayConstants.SIGNATURE.equals(en.getKey().trim())) {
				continue;
			}
			tree.put(en.getKey(), en.getValue());
		}
		it = tree.entrySet().iterator();
		StringBuffer sf = new StringBuffer();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			sf.append(en.getKey() + UnionpayConstants.EQUAL + en.getValue()
					+ UnionpayConstants.AMPERSAND);
		}
		return sf.substring(0, sf.length() - 1);
	}


	/**
	 * 兼容老方法 将形如key=value&key=value的字符串转换为相应的Map对象
	 * 
	 * @param result
	 * @return
	 */
	public static UnionpayHashMap coverResultStringToMap(String result) {
		return convertResultStringToMap(result);
	}

	/**
	 * 将形如key=value&key=value的字符串转换为相应的Map对象
	 * @param result
	 * @return
	 */
	public static UnionpayHashMap convertResultStringToMap(String result) {
		UnionpayHashMap map = null;

		if (!StringUtils.isEmpty(result)) {
			if (result.startsWith(UnionpayConstants.LEFT_BRACE) && result.endsWith(UnionpayConstants.RIGHT_BRACE)) {
				result = result.substring(1, result.length() - 1);
			}
			map = parseQString(result);
		}
		return map;
	}


	/**
	 * 解析应答字符串，生成应答要素
	 * @param str 需要解析的字符串
	 * @return 解析的结果map
	 * @throws UnsupportedEncodingException
	 */
	public static UnionpayHashMap parseQString(String str) {
		UnionpayHashMap map = new UnionpayHashMap();
		int len = str.length();
		StringBuilder temp = new StringBuilder();
		char curChar;
		String key = null;
		boolean isKey = true;
		boolean isOpen = false; // 值里有嵌套
		char openName = 0;
		if(len>0){
			for (int i = 0; i < len; i++) { // 遍历整个带解析的字符串
				curChar = str.charAt(i);// 取当前字符
				if (isKey) { // 如果当前生成的是key					
					if (curChar == '=') { // 如果读取到=分隔符 
						key = temp.toString();
						temp.setLength(0);
						isKey = false;
					} else {
						temp.append(curChar);
					}
				} else  {// 如果当前生成的是value
					if(isOpen){
						if(curChar == openName){
							isOpen = false;
						}						
					}else{//如果没开启嵌套
						if(curChar == '{'){//如果碰到，就开启嵌套
							isOpen = true;
							openName ='}';
						}
						if(curChar == '['){
							isOpen = true;
							openName =']';
						}
					}					
					if (curChar == '&' && !isOpen) { // 如果读取到&分割符,同时这个分割符不是值域，这时将map里添加
						putKeyValueToMap(temp, isKey, key, map);
						temp.setLength(0);
						isKey = true;
					} else {
						temp.append(curChar);
					}
				}				
			}
			putKeyValueToMap(temp, isKey, key, map);
		}
		return map;
	}

	private static void putKeyValueToMap(StringBuilder temp, boolean isKey,
			String key, UnionpayHashMap map) {
		if (isKey) {
			key = temp.toString();
			if (key.length() == 0) {
				throw new RuntimeException("QString format illegal");
			}
			map.put(key, "");
		} else {
			if (key.length() == 0) {
				throw new RuntimeException("QString format illegal");
			}
			map.put(key, temp.toString());
		}
	}
	/**
	 * map转换java对象
	 * @param map
	 * @param beanClass
	 * @return java对象
	 * @throws Exception
	 */	
	@SuppressWarnings("unchecked")
	public static <T extends Object> T mapToObject(Map<?, ?> map, Class<?> beanClass) throws Exception {    
		if (map == null) {
			return null;    	        	
		} 	  
		T obj = (T) beanClass.newInstance();  	  
		Field[] childFields = obj.getClass().getDeclaredFields();
		Field[] superFields = obj.getClass().getSuperclass().getDeclaredFields();
		for (Field field : childFields) {    
			int mod = field.getModifiers();    
			if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {    
				continue;    
			}      
			field.setAccessible(true);
			field.set(obj, map.get(field.getName()));   
		}   
		for (Field field : superFields) {    
			int mod = field.getModifiers();    
			if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {    
				continue;    
			}    
			field.setAccessible(true);    
			field.set(obj, map.get(field.getName()));   
		}   
		return obj;    
	}

	/**
	 * java bean 转化 map
	 * @param javaBean
	 * @return map
	 * @throws Exception 
	 */
	public static UnionpayHashMap beanToMap(Object javaBean) throws Exception {
		UnionpayHashMap hashMap=new UnionpayHashMap();
		// 获取javaBean属性
		BeanInfo beanInfo = Introspector.getBeanInfo(javaBean.getClass());
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		if (propertyDescriptors != null && propertyDescriptors.length > 0) {
			String propertyName = null; // javaBean属性名
			Object propertyValue = null; // javaBean属性值
			for (PropertyDescriptor pd : propertyDescriptors) {
				propertyName = pd.getName();
				if (!propertyName.equals("class")) {
					Method readMethod = pd.getReadMethod();
					propertyValue = readMethod.invoke(javaBean, new Object[0]);
					hashMap.put(propertyName, propertyValue);
				}
			}
		}
		return hashMap;
	}

	/**
	 * 商户发送交易时间 格式:yyyyMMddHHmmss
	 * @return
	 */
	public static String getCurrentTime() {
		return new SimpleDateFormat(UnionpayConstants.DATE_TIME_FORMAT).format(new Date());
	}
	
	/**
	 * 解压zip
	 * @param zipFilePath
	 * @param outPutDir
	 * @return 文件列表
	 */
	public static List<String> unzip(String zipFilePath,String outPutDir) {
		List<String> fileList = new ArrayList<String>();
		try {
			ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFilePath)); // 输入源zip路径  
			BufferedInputStream bin = new BufferedInputStream(zin);
			BufferedOutputStream bout = null;
			File file = null;  
			ZipEntry entry;
			try {
				while ((entry = zin.getNextEntry()) != null && !entry.isDirectory()) {
					file = new File(outPutDir, entry.getName());  
					if (!file.exists()) {  
						new File(file.getParent()).mkdirs();  
					}
					bout = new BufferedOutputStream(new FileOutputStream(file));  
					int b;
					while ((b = bin.read()) != -1) {  
						bout.write(b);  
					}
					bout.flush();
					fileList.add(file.getAbsolutePath());
				}
				getLogger().info(zipFilePath + " unzip succeed.");
			} catch (IOException e) {  
				getLogger().error(zipFilePath + " unzip error", e);
			}finally{
				try {
					bin.close();
					zin.close();
					if(bout!=null){
						bout.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}  
			}
		} catch (FileNotFoundException e) {
			getLogger().error(zipFilePath + " file not found", e); 
		}
		return fileList;
	}

}
